package com.yugao.fintech.antelope.base.tenant.core.utils;

import com.yugao.fintech.antelope.base.exception.enums.GlobalErrorEnum;
import com.yugao.fintech.antelope.base.model.constants.RequestCons;
import com.yugao.fintech.antelope.base.model.constants.TenantCons;
import com.yugao.fintech.antelope.base.model.module.tenant.TenantContextHolder;
import com.yugao.fintech.framework.assistant.utils.exception.BaseException;
import com.yugao.fintech.framework.assistant.utils.exception.BizException;
import lombok.extern.slf4j.Slf4j;

import java.util.Map;
import java.util.concurrent.Callable;

/**
 * 多租户 Util
 */
@Slf4j
public class TenantUtils {

    /**
     * 使用指定租户，执行对应的逻辑
     * <p>
     * 注意，如果当前是忽略租户的情况下，会被强制设置成不忽略租户
     * 当然，执行完成后，还是会恢复回去
     *
     * @param tenantId 租户编号(子租户)
     * @param runnable 逻辑
     */
    public static void execute(Long tenantId, Runnable runnable) {
        Long oldTenantId = TenantContextHolder.get().getTenantId();
        boolean oldFilterTenant = TenantContextHolder.get().isFilterTenant();
        try {
            TenantContextHolder.get().setTenantId(tenantId);
            TenantContextHolder.get().filterTenant(true);
            // 执行逻辑
            runnable.run();
        } finally {
            TenantContextHolder.get().setTenantId(oldTenantId);
            TenantContextHolder.get().filterTenant(oldFilterTenant);
        }
    }

    /**
     * 使用指定租户，执行对应的逻辑
     * <p>
     * 注意，如果当前是忽略租户的情况下，会被强制设置成不忽略租户
     * 当然，执行完成后，还是会恢复回去
     *
     * @param tenantId 租户编号(子租户)
     * @param callable 逻辑
     */
    public static <V> V execute(Long tenantId, Callable<V> callable) {
        Long oldTenantId = TenantContextHolder.get().getTenantId();
        boolean oldFilterTenant = TenantContextHolder.get().isFilterTenant();
        try {
            TenantContextHolder.get().setTenantId(tenantId);
            TenantContextHolder.get().filterTenant(true);
            // 执行逻辑
            return callable.call();
        } catch (Exception e) {
            if (e instanceof BaseException) {
                throw (BaseException) e;
            }
            throw new RuntimeException(e);
        } finally {
            TenantContextHolder.get().setTenantId(oldTenantId);
            TenantContextHolder.get().filterTenant(oldFilterTenant);
        }
    }

    /**
     * 使用指定租户，执行对应的逻辑
     * <p>
     * 注意，如果当前是忽略租户的情况下，会被强制设置成不忽略租户
     * 当然，执行完成后，还是会恢复回去
     *
     * @param tenantId 租户编号
     * @param callable 逻辑
     */
    public static <V> V execute(TenantCons.Type tenantType, Long tenantId, Callable<V> callable) {
        Long oldTenantId = TenantContextHolder.get().getTenantId();
        Long oldMainTenantId = TenantContextHolder.get().getMainTenantId();
        boolean oldFilterTenant = TenantContextHolder.get().isFilterTenant();
        boolean oldFilterMainTenant = TenantContextHolder.get().isFilterMainTenant();
        try {
            configFilterTenantId(tenantType, tenantId);
            configFilterTenant(tenantType, false);
            // 执行逻辑
            return callable.call();
        } catch (Exception e) {
            if (e instanceof BaseException) {
                throw (BaseException) e;
            }
            log.error("error: ", e);
            throw new BizException(GlobalErrorEnum.FAIL);
        } finally {
            TenantContextHolder.get().setTenantId(oldTenantId);
            TenantContextHolder.get().setMainTenantId(oldMainTenantId);
            TenantContextHolder.get().filterTenant(oldFilterTenant);
            TenantContextHolder.get().filterMainTenant(oldFilterMainTenant);
        }
    }

    public static void execute(TenantCons.Type tenantType, Long tenantId, Runnable runnable) {
        execute(tenantType, tenantId, () -> {
            runnable.run();
            return null;
        });
    }

    /**
     * 忽略租户，执行对应的逻辑, 真对子租户
     *
     * @param runnable 逻辑
     */
    public static void executeIgnore(Runnable runnable) {
        boolean oldFilterTenant = TenantContextHolder.get().isFilterTenant();
        try {
            TenantContextHolder.get().filterTenant(false);
            // 执行逻辑
            runnable.run();
        } finally {
            TenantContextHolder.get().filterTenant(oldFilterTenant);
        }
    }

    /**
     * 忽略租户，执行对应的逻辑, 真对子租户
     *
     * @param tenantType 租户类型
     * @param runnable   逻辑
     */
    public static void executeIgnore(TenantCons.Type tenantType, Runnable runnable) {
        boolean oldTenant = TenantContextHolder.get().isFilterTenant();
        boolean oldMainTenant = TenantContextHolder.get().isFilterMainTenant();
        try {
            configFilterTenant(tenantType, true);
            // 执行逻辑
            runnable.run();
        } finally {
            TenantContextHolder.get().filterTenant(oldTenant);
            TenantContextHolder.get().filterTenant(oldMainTenant);
        }
    }

    public static <T> T executeIgnore(TenantCons.Type tenantType, Callable<T> callable) {
        boolean oldTenant = TenantContextHolder.get().isFilterTenant();
        boolean oldMainTenant = TenantContextHolder.get().isFilterMainTenant();
        try {
            configFilterTenant(tenantType, true);
            // 执行逻辑
            return callable.call();
        } catch (Exception e) {
            if (e instanceof BaseException) {
                throw (BaseException) e;
            }
            log.error("error: ", e);
            throw new BizException(GlobalErrorEnum.FAIL);
        } finally {
            TenantContextHolder.get().filterTenant(oldTenant);
            TenantContextHolder.get().filterTenant(oldMainTenant);
        }
    }

    private static void configFilterTenant(TenantCons.Type tenantType, boolean isIgnore) {
        if (tenantType == TenantCons.Type.ALL) {
            TenantContextHolder.get().filterTenant(!isIgnore);
            TenantContextHolder.get().filterMainTenant(!isIgnore);
            return;
        }
        TenantContextHolder.get().filterMainTenant(false);
        if (tenantType == TenantCons.Type.SUB) {
            TenantContextHolder.get().filterTenant(!isIgnore);
            return;
        }

        TenantContextHolder.get().filterTenant(false);
        if (tenantType == TenantCons.Type.MAIN) {
            TenantContextHolder.get().filterMainTenant(!isIgnore);
        }
    }

    private static void configFilterTenantId(TenantCons.Type tenantType, Long tenantId) {
        if (tenantType == TenantCons.Type.SUB) {
            TenantContextHolder.get().setTenantId(tenantId);
        } else if (tenantType == TenantCons.Type.MAIN) {
            TenantContextHolder.get().setMainTenantId(tenantId);
        } else if (tenantType == TenantCons.Type.ALL) {
            TenantContextHolder.get().setMainTenantId(tenantId);
            TenantContextHolder.get().setTenantId(tenantId);
        }
    }

    /**
     * 针对子租户
     */
    public static <V> V executeIgnore(Callable<V> callable) {
        boolean oldFilterTenant = TenantContextHolder.get().isFilterTenant();
        try {
            TenantContextHolder.get().filterTenant(false);
            // 执行逻辑
            return callable.call();
        } catch (Exception e) {
            throw new RuntimeException(e);
        } finally {
            TenantContextHolder.get().filterTenant(oldFilterTenant);
        }
    }

    /**
     * 将多租户编号，添加到 header 中
     *
     * @param headers HTTP 请求 headers
     */
    public static void addTenantHeader(Map<String, String> headers) {
        Long tenantId = TenantContextHolder.get().getTenantId();
        if (tenantId != null) {
            headers.put(RequestCons.Headers.TENANT_ID, tenantId.toString());
        }
    }

}
